#include <cstdlib>
#include <iostream>
#include <NTL/ZZ_p.h>
#include <NTL/ZZ.h>
#include "faultedSig.cpp"
#include <openssl/sha.h>
#include <inttypes.h>
#include <vector>

using namespace std;
using namespace sig;

void WordSizeConfig()
{
	if(wordsize == 8)
	{
		wordcut = 0xff;
		return;
	}
	if(wordsize == 16)
	{
		wordcut = 0xffff;
		return;
	}
	if(wordsize == 32)
	{
		wordcut = 0xffffffff;
		return;
	}
	if(wordsize == 64)
	{
		wordcut = 0xffffffffffffffff;
		return;
	}
	cerr << "WordSize not correctly initialized:" <<endl; 
	cerr << "possible values: 8, 16, 32, 64 bit" << endl;
	exit(-1);
}

// Yx and Yy constitute the public key Y=[d]G
template <typename Ty>
static GigInt<Ty> keyRetrieval(GigInt_p<Ty> &Yx, GigInt_p<Ty> &Yy)
{
	string n_s = "6277101735386680763835789423176059013767194773182842284081";
	string p_s = "6277101735386680763835789423207666416083908700390324961279";
	string gx_s = "188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012";
	string gy_s = "07192b95ffc8da78631011ed6b24cdd573f977a11e794811";		
	GigInt<Ty> d,e,m;
	GigInt<Ty> n(n_s);
	GigInt_p<Ty>Gx(gx_s,p_s);
	GigInt_p<Ty>Gy(gy_s,p_s);
	string zero("0");
	GigInt_p<Ty>Rx(zero,p_s);
	GigInt_p<Ty>Ry(zero,p_s);
	unsigned int t = n.size();
	unsigned int ind =0;
	
	//t-1 because an empty GigInt is initialized with a vector of 0 value
	for(int i=0;i<t-1;++i)
		d.chunks.push_back(0);
	
	if(d.size()!= t)
		cout << "d init error" << endl;

	m.randGigInt("0","p_s");
	e = sig::hash(m);

	sig::scalar_mul<Ty>(d,Gx,Gy,&Rx,&Ry);
//	while(!sig::pointsEqual(Rx,Ry,Yx,Yy))
	{
		sig::sig_token<Ty> st = sig::faulted_signature_gen(m,d, ind);	
		sig::scalar_mul<Ty>(d,Gx,Gy,&Rx,&Ry);
	}
	return d;
}		

#define WORD uint8_t
int main (int argc, char **argv)
{
	
	GigInt<WORD> mesg("1123793769426349210237129837694264469246127");
	GigInt<WORD> priv_key("172197391687615531287131937273462623763493\
			4129461833");
	sig_token<WORD> token;
	token = signature_gen(mesg, priv_key);
#ifdef DEBUG
#define WORD2 uint64_t
	GigInt<WORD2>mesg2(mesg.chunks);
	GigInt<WORD2>priv_key2(priv_key.chunks);
	sig_token<WORD2> token2;
	vector<WORD> temp = token.r.getNum().chunks;
	GigInt<WORD2> r_num(temp);
	temp = token.s.getNum().chunks;
	GigInt<WORD2> s_num(temp);
	temp = token.r.getP().chunks;
	GigInt<WORD2> n2(temp);
	GigInt_p<WORD2> r2(r_num, n2);
	GigInt_p<WORD2> s2(s_num, n2);
	token2.r = r2;
	token2.s = s2;
	//cerr << "mesg "; mesg2.dump();
	//cerr << "mesg "; mesg.dump();
	//cerr << "priv_key "; priv_key2.dump();
	//cerr << "priv_key "; priv_key.dump();
	//cerr << "token.r "; token2.r.getNum().dump();
	//cerr << "token.r "; token.r.getNum().dump();
	//cerr << "token.s "; token2.s.getNum().dump();
	//cerr << "token.s "; token.s.getNum().dump();

	if(!sig_verification<WORD2>(token2, priv_key2, mesg2))
	{
		cerr << "Signature verification failed in "<< (sizeof(WORD2) *8)
			<< "-bit mode" << endl;
		throw fatal;
	}
#endif
	if(sig_verification(token,priv_key,mesg))
		cout << "Signature Generation and Verification correct in " << 
			(sizeof(WORD) * 8) <<	"-bit mode!" << endl;
	else 
		cout << "Dammit!!!" << endl;



	//GigInt_p<WORD> kx,ky;
	
	//	keyRetrieval(kx,ky);	
	
	//string first_s = "5181453190987320882218365668432465446752707461787486894";
	//string second_s ="3367833069869449146798863467724947988115347375352874683079";
	//GigInt<uint8_t> first(first_s);
	//GigInt<uint8_t> second(second_s);
	//GigInt<uint8_t> result;
	//result = first * second;
	//cerr << second.bitSize() << endl;
	//cerr << "result   "; result.dump();
	//cerr << result.chunks.size() * 8 << endl;
	//ZZ first_z, second_z, result_z;
  	//first_z = to_ZZ(first_s.c_str());
	//second_z = to_ZZ(second_s.c_str());
	//result_z = first_z * second_z;
	//cerr << "result_z " << result_z << endl;
	//
//#define WORD2 uint32_t

	//string d_s("9857985443745628973653745693723946982356976234256239472");
	//GigInt<WORD> d(d_s);
	//GigInt<WORD> zero(utl::zero);
	//GigInt<WORD> p(utl::p_s);
	//GigInt_p<WORD> Gx(utl::gx_s, p);
	//GigInt_p<WORD> Gy(utl::gy_s, p);
	//GigInt_p<WORD> r1x(zero, p), r1y(zero, p);
	//scalar_mul(d, Gx, Gy, &r1x, &r1y);
	//cerr << "x "; r1x.getNum().dump();
	//cerr << "y "; r1y.getNum().dump();
	
	//GigInt<WORD2> d2(d_s);
	//GigInt<WORD2> zero2(utl::zero);
	//GigInt<WORD2> p2(utl::p_s);
	//GigInt_p<WORD2> Gx2(utl::gx_s, p2);
	//GigInt_p<WORD2> Gy2(utl::gy_s, p2);
	//GigInt_p<WORD2> r2x(zero2, p2), r2y(zero2, p2);
	//scalar_mul(d2, Gx2, Gy2, &r2x, &r2y);
	//cerr << "x "; r2x.getNum().dump();
	//cerr << "y "; r2y.getNum().dump();
}




